home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-05-05 | 16.1 KB | 408 lines | [TEXT/R*ch] |
- {©1994 by Sean Crist (kurisuto@chopin.udel.edu). (Soon I'll have a new address on some machine}
- {in the domain upenn.edu; try reaching me there if chopin.udel.edu doesn't work.) I'd appreciate any comments or }
- {bug reports. This code may be freely used for any purpose, except that military use is expressly prohibited.}
- {--------------------------------------------------------------------------------------------------}
-
-
- {The purpose of the following code may not be immediately apparent, but it can make life a LOT easier if you're}
- {writing an application which can have any number of open windows of multiple types (ResEdit is a good example}
- {of this kind of application). }
-
- {Basically, this code offers a way of putting a little ID collar around the neck of each window. This way, you}
- {don't need to keep WindowPtr's to all your windows; it's like letting the dogs off their leashes.}
-
- {The hard part is figuring out exactly how to use this unit, but if you've got the patience to do that, you're in good shape.}
-
-
- unit SeansWindowManager;
-
- { What this code is for: This is basically an extension to the Window Manager 1) to support floating palettes, and}
- { 2) to eliminate the need to keep WindowPtr's to each window. If you are writing a *simple* application with a fixed }
- {number of windows, you don't need this code; you can just define your WindowPtr's as globals. But if your application }
- {supports arbitrary numbers of windows (e.g. a word processor which allows any number of documents to be open at a }
- {time), it can be a hassle to keep track of all of these WindowPtr's. The following code makes it easy to manage large }
- {numbers of windows of assorted kinds. }
- { The basic strategy of these routines is to put a handle in the RefCon of every window opened by your application }
- {(not only your 'document' windows but also your Clipboard window, your modeless dialogs, etc). The structure of}
- {the data in this handle is defined below; it contains information uniquely identifying the window. You don't need to keep}
- {any pointers to specific windows.}
- { Of course, you need not be greatly concerned with these RefCon handles; just make the appropriate calls to the}
- {routines below, and most of the grubby work will be done for you.}
-
- {There's several changes you must make to your code, and to the code below, for this all to work properly:}
- {1. You must edit the list of wTypes below to reflect all the kinds of windows your program supports. E.g. ResEdit}
- { might have wClipboard = 1, wTEXTeditor = 2, wICONeditor = 3, etc. for all its kinds of windows.}
- {2. You must edit the SetPrivateHilite routine below to reflect the relation between palettes and regular}
- { windows in a way specific to your application. For example, if you want your wToolsPalette to become}
- { visible when one of your wArtWindows is selected, you should code this here.}
- {3. Your program's initialization routine must set the PrivateFrontWindow global (declared below) to some}
- { valid WindowPtr. It doesn't matter which window this is, as long as it is a valid one. The code below}
- { will change the PrivateFrontWindow in response to your calls, but it needs a window to start out with.}
- {4. Every time you open a new window, you must call InitializeWindow on it to record what kind of window it is.}
- { Just before you dispose of a window, you must call DisposeWData on it first.}
- {5. You must not ever call the Toolbox routine SelectWindow, since this will mess up the hiliting of windows and }
- { visibility of palettes. Use the routine PrivateActivate (defined below) in place of SelectWindow.}
- {6. In your event loop, you need to do a little extra work to support floating palettes. We treat a window and }
- { its palettes as one 'layer'. When you get a mousedown in PrivateFrontWindow, or in one of the palettes}
- { belonging to PrivateFrontWindow, then you should just call BringToFront on that window.}
- { (Your event code thus needs to be smart about what kinds of palettes belong to what kinds }
- { of windows.)}
- { If the mousedown is in some other window (i.e. neither PrivateFrontWindow nor its palettes), then you should}
- { call PrivateActivate, which is much like SelectWindow except that it does the extra work to support floating}
- { palettes and make sure everything gets hilited properly.}
-
- {I know this all sounds horribly complicated, but if you look over this code for a little, I think it will start to make}
- {sense.}
-
-
- interface
-
- {Sample window types}
- const
- wMiscWindow = 1;
- wMiscWindowWithPalette = 2;
- wMiscPalette = 3;
- {These window types are just to illustrate how this code works. In your program, you would want to change these}
- {constants to whatever kinds of windows your program supports. (The application I'm working on has about 15}
- {different kinds of windows and three kinds of palettes, for example.) Each window type, including palettes, must}
- {have a unique identifying number.}
-
- const
- BadWindow = 9999;
- {For doing error checking. We send this to our own error handlers, pretending it's a real Toolbox error code.}
-
- var
- PrivateFrontWindow: WindowPtr;
- {This is the WindowPtr to the window that we _consider_ to be the front window, ignoring whatever floating palettes}
- {might be in front of it in the actual window list. Note: you must initialize this to some valid WindowPtr when your}
- {program initializes itself, or this code will crash.}
-
-
-
- {We store the following record as a handle in the RefCon field of a window. It contains all of our information that}
- {we need to keep track of what kind of window this is. Most of the time, you'll never need to manipulate the contents}
- {of this record directly, since there's more convenient routines below to do this work for you.}
- type
- WindowData = record
- Class: Integer; {What kind of window or dialog? This is one of the wType constants above.}
- Owner: Integer;
- Index: Integer;
- DataHandle: Handle; {Handle to whatever other data you wish to store for this window.}
- AuxData: Integer; {To be used however you like.}
- end;
- WDataPtr = ^WindowData;
- WDataHandle = ^WDataPtr;
- { Note on the fields 'Class', 'Owner', and 'Index': the Class is the kind of window, e.g. a wClipboard window,}
- {a search-and-replace modeless dialog, or whatever. This field contains one of the wType constants defined above.}
- {The owner and index tell which window of that kind we are talking about. In the case where there's only one }
- {instance of a kind of window (such as a Clipboard window), you can just set Owner and Index to 0 when you call }
- {InitializeWindow. When there's more than one instance of a particular window type, give each window a different }
- {Index to distinguish it.}
- { Under normal circumstances, you can just set Owner to 0 in all cases. I include this field because my application }
- {has script windows for different kinds of objects, so I need to know if this is, say, the script window for}
- {the 7th zone or the 7th room or whatever.}
-
-
-
- {Call InitializeWindow when you are opening a new window. You are responsible for creating the window with}
- {GetNewCWindow or GetNewDialog or whatever; pass this handle in WhichWindow. InitializeWindow allocates}
- {a WDataHandle, installs it in the refCon of whichWindow, and sets up the Class, Owner, and Index values.}
- function InitializeWindow (whichWindow: WindowPtr; Class, Owner, Index: Integer): Boolean;
-
- {Dispose the WDataHandle associated with whichWindow. Note: You are responsible for disposing whatever}
- {data you have stored in DataHandle. The right order to dispose things is: 1) dispose any data of your own}
- {which you've stored in DataHandle 2) call DisposeWData 3) dispose of the window or dialog itself with}
- {the appropriate Toolbox call.}
- procedure DisposeWData (whichWindow: WindowPtr);
-
- {Store this handle in DataHandle.}
- procedure SetWHandle (whichWindow: WindowPtr; TheHandle: Handle);
-
- {Return the handle stored in DataHandle.}
- function GetWHandle (whichWindow: WindowPtr): Handle;
-
- {Given a window, return its WDataHandle}
- function GetWData (whichWindow: WindowPtr): WDataHandle;
-
- {What is the class of whichWindow? (You could accomplish this same thing by calling GetWData and then}
- {looking at MyWData^^.Class, but this routine saves work.) Returns -1 if the WData is bad (i.e., you probably}
- {didn't call InitializeWindow on this window.}
- function GetWClass (whichWindow: WindowPtr): Integer;
-
- {What is the index of whichWindow? (You could accomplish this same thing by calling GetWData and then}
- {looking at MyWData^^.Index, but this routine saves work.) Returns -1 if the WData is bad (i.e., you probably}
- {didn't call InitializeWindow on this window.}
- function GetWIndex (whichWindow: WindowPtr): Integer;
-
- {Search through all the windows and try to find the one which has the specified class, owner, and index.}
- function SearchWindow (Class, Owner, Index: Integer): WindowPtr;
-
- {Same as SearchWindow, but for windows like the Clipboard which have only one instance.}
- function FindUniqueWindow (Class: Integer): WindowPtr;
-
- {Try to bring to front the window with this class, owner, and index. Returns TRUE if there is no such window,}
- {i.e. the window can't be activated and must be created. This can make your code succinct; when the user}
- {does some action to open a particular window, your routine MyOpenWindow can start out 'if CantActivate}
- {(the window I need) then (do whatever to open it, including calling InitializeWindow)'.}
- function CantActivate (Class, Owner, Index: Integer): Boolean;
-
- {You should use this instead of SelectWindow.}
- procedure PrivateActivate (whichWindow: WindowPtr);
-
-
- implementation
-
- procedure doOSErr (ErrorCode: Integer);
- begin
- {Handle the error however you like. I usually post an alert.}
- Sysbeep(1);
- end;
-
- {Error checking. Make sure the WDataHandle is a valid one by checking its length. This is}
- {mainly for debugging.}
- function IsValidWindowHandle (TheWDataHandle: WDataHandle): Boolean;
- var
- HandleSize: LongInt;
- begin
- IsValidWindowHandle := TRUE;
- HandleSize := GetHandleSize(Handle(TheWDataHandle));
- if HandleSize <> SizeOf(WindowData) then
- IsValidWindowHandle := FALSE;
- end;
-
-
- function GetWData (whichWindow: WindowPtr): WDataHandle;
- var
- thePeek: WindowPeek;
- TheHandle: Handle;
- begin
- if WhichWindow = nil then
- begin
- doOSErr(BadWindow);
- GetWData := nil;
- end
- else
- begin
- thePeek := WindowPeek(WhichWindow);
- TheHandle := Handle(thePeek^.RefCon);
- GetWData := WDataHandle(TheHandle);
- end;
- end;
-
-
- function GetWClass (whichWindow: WindowPtr): Integer;
- var
- WData: WDataHandle;
- begin
- WData := GetWData(whichWindow);
- if IsValidWindowHandle(WData) then
- GetWClass := WData^^.Class
- else
- GetWClass := -1;
- end;
-
-
- function GetWIndex (whichWindow: WindowPtr): Integer;
- var
- WData: WDataHandle;
- begin
- WData := GetWData(whichWindow);
- if IsValidWindowHandle(WData) then
- GetWIndex := WData^^.Index
- else
- GetWIndex := -1;
- end;
-
-
- function GetWHandle (whichWindow: WindowPtr): Handle;
- var
- WData: WDataHandle;
- begin
- WData := GetWData(whichWindow);
- if IsValidWindowHandle(WData) then
- GetWHandle := WData^^.DataHandle
- else
- begin
- GetWHandle := nil;
- doOSErr(BadWindow);
- end;
- end;
-
-
- procedure SetWHandle (whichWindow: WindowPtr; TheHandle: Handle);
- var
- WData: WDataHandle;
- begin
- WData := GetWData(whichWindow);
- if IsValidWindowHandle(WData) then
- WData^^.DataHandle := TheHandle
- else
- doOSErr(BadWindow);
- end;
-
-
-
- function InitializeWindow (whichWindow: WindowPtr; Class, Owner, Index: Integer): Boolean;
- var
- thePeek: WindowPeek;
- WData: WDataHandle;
- OkSoFar: Boolean;
- Result: Integer;
- begin
- OkSoFar := true;
- thePeek := WindowPeek(WhichWIndow);
- WData := WDataHandle(NewHandle(SizeOf(WindowData)));
- Result := MemError;
- if Result <> 0 then
- begin
- OkSoFar := False;
- doOSErr(MemError);
- end;
-
- if OkSoFar then
- begin
- thePeek^.RefCon := LongInt(WData);
- WData^^.Class := Class;
- WData^^.Owner := Owner;
- WData^^.Index := Index;
- end;
-
- InitializeWindow := OkSoFar;
- end;
-
-
- procedure DisposeWData (whichWindow: WindowPtr);
- var
- WData: WDataHandle;
- begin
- WData := GetWData(whichWindow);
- if IsValidWindowHandle(WData) then
- DisposHandle(Handle(WData))
- else
- doOSErr(BadWindow);
- end;
-
-
- {I hope this is still a kosher way to find out which window is really in the front! If this is no longer}
- {kosher, then I'd just make an invisible window which is always frontmost (a la BringToFront) and}
- {use it as my starting point for the search through the linked list.}
- function findFrontWindow: WindowPtr;
- const
- WindowList = $9D6;
- type
- PtrPtr = ^Ptr;
- var
- WindowListPtr: PtrPtr;
- begin
- WindowListPtr := Pointer(WindowList);
- findFrontWindow := WindowPtr(WindowListPtr^); {was: FrontWindow}
- end;
-
-
- function SearchWindow (Class, Owner, Index: Integer): WindowPtr;
- var
- done: boolean;
- ThisWindow, FoundWindow: WindowPtr;
- WData: WDataHandle;
- ThisWPeek: WindowPeek;
- begin
- done := false;
- FoundWindow := nil;
- ThisWindow := FindFrontWindow;
- if ThisWindow <> nil then
- while not done do
- begin
- WData := GetWData(ThisWindow);
- if IsValidWindowHandle(WData) then
- if WData^^.Class = Class then
- if WData^^.Owner = Owner then
- if WData^^.Index = Index then
- begin
- FoundWindow := ThisWindow;
- Done := true;
- end;
- if not Done then
- begin
- ThisWPeek := WindowPeek(ThisWindow);
- ThisWPeek := ThisWPeek^.nextWindow;
- ThisWindow := WindowPtr(ThisWPeek);
- if ThisWindow = nil then
- Done := true;
- end;
- end;
- SearchWindow := FoundWindow;
- end;
-
-
- function CantActivate (Class, Owner, Index: Integer): Boolean;
- var
- ThisWindow: WindowPtr;
- begin
- CantActivate := TRUE; {Assume the window doesn't exist.}
- ThisWindow := SearchWindow(Class, Owner, Index);
- if ThisWindow <> nil then
- begin
- SelectWindow(ThisWindow);
- CantActivate := FALSE; {The window exists, so we _can_ activate it.}
- end;
- end;
-
- function FindUniqueWindow (Class: Integer): WindowPtr;
- begin
- FindUniqueWindow := SearchWindow(Class, 0, 0);
- {By convention, all unique windows have 0 and 0 as their other arguments.}
- end;
-
- procedure AutoPositionPalettes (WhichWindow: WindowPtr);
- begin
- {If you want your palettes to be automatically positioned next to a certain kind of window, put code here}
- {to move the palettes to the right position.}
- end;
-
- {This internal routine is used to hilite or unhilite windows. Note that there's more going on here than}
- {just drawing the hiliting on the window. For our purposes, 'hilite' means 'bring it to the front, hilite it,}
- {and display its palettes as well'. 'Unhilite' means 'Turn off its hiliting, and hide its palettes also.'}
- procedure SetPrivateHilite (whichWindow: WindowPtr; Hilite: Boolean);
- begin
- if WhichWindow = nil then
- doOSErr(BadWindow);
- if Hilite then
- begin
- BringToFront(whichWindow);
- ShowHide(whichWindow, TRUE);
- end;
- HiliteWindow(whichWindow, Hilite);
- {If this window has a palette, bring it to the front as well.}
- case GetWClass(whichWindow) of
- {You should modify the case selectors here to handle whatever kinds of windows with palettes your application supports.}
- wMiscWindowWithPalette:
- begin
- if Hilite then
- begin
- AutoPositionPalettes(whichWindow);
- BringToFront(FindUniqueWindow(wMiscPalette));
- end;
- ShowHide(FindUniqueWindow(wMiscPalette), Hilite);
- end;
- otherwise
- {If this window owns no palettes, do nothing.}
- end;
- end;
-
- {Since we're using windows in a slightly unorthodox way, we can't rely on the Activate and Deactivate events}
- {to do our work for us. doSendPrivateActivateMessage is the routine to handle our equivalent of an Activate}
- {event for the PrivateFrontWindow. (Palettes can never be the PrivateFrontWindow, by definition.)}
- procedure doSendPrivateActivateMessage (whichWindow: WindowPtr);
- external;
-
- procedure privateActivate (whichWindow: WindowPtr);
- begin
- SetPrivateHilite(PrivateFrontWindow, FALSE);
- PrivateFrontWindow := whichWindow;
- SetPrivateHilite(PrivateFrontWindow, TRUE);
- {Then send a message to update menus, etc.}
- doSendPrivateActivateMessage(PrivateFrontWindow);
- end;
-
- end.
-